// NIRMA STUDIO, 2013
// Object INIT
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cstdlib>
#include <math.h>
#include <iomanip>
#include "inc.h"


using namespace std;


class Object {
	public:
		int 		cordX, 		cordY, 			distance;
		int 		level,  	crit_chance, 	dodge_chance,   max_level;
		int 		move_speed, attack_range, 	sight_range, 	cost[3];
		string 		name, 		armor_type, 	attack_type[2], race;
		float 		attack, 	health, 		mana, 	 		attack_speed;
		float 		hp_regen, 	mana_regen, 	experience, 	exp_amount;
		bool 		in_sight, 	have_target, 	horrified;
		bool 		x,				y;

		int 		max_sight_range,	max_attack_range, 	max_move_speed;
		float 		max_experience,		max_attack,			max_hp_regen;
		float 		max_mana_regen,		max_health,			max_mana;

		Object();
		~Object();
		void 	Stat(); // вывод полной статистики объекта
		void 	ViewHp(); // вывод состояния здоровья объекта
		void 	Take_dmg(float dmg, string attack, int crit); 	// получение объектом урона
		void 	Gain_exp(float x); 	// передача опыта объекту
		void 	Gain_level(); 		// повышение уровня объекта
		bool 	TargetInSight(int distance, int sight_distance); // определение является ли цель видимой

		float 	Get_exp_amount(Object *target); // получение индекса опыта объекта
		bool 	DeadOrNot(Object *target); // мертв?	
		bool	TargetInAttackRange(Object *target1, Object *target2);	//может ли target1 нанести повреждение	target2	
		int 	RangeDetect(Object *target1, Object* target2);	// определение дистанции до цели
		bool 	TargetIsEnemy(Object *target1, Object *target2); // является ли цель врагом
		void 	Fight(Object *target1, Object *target2); // цикл боя	
		
		void 	Buf(Object *target, int arg, string type, float amount); // баф или дебаф на объект
};

Object::Object(){}

Object::~Object(){}

void Object::Stat(){
	cout << endl;
	cout << "Name:      	" 	<< setw(10) << name                   << endl;
	cout << "Level:     	" 	<< setw(10) << level                  << endl;
	cout << "Health:    	" 	<< setw(10) << float(health)          << endl;
	cout << "Mana:      	" 	<< setw(10) << float(mana)            << endl;
	cout << "Attack:    	" 	<< setw(10) << float(attack)          << endl;
	cout << "Attack Type:   "  	<< setw(11) << attack_type[0]	      << endl;
	cout << "Armor Type:    "	<< setw(11) << armor_type[0]	      << endl;
	cout << "Crit Chance:   "  	<< setw(11) << crit_chance 	      << endl;
	cout << "Dodge Chance:  "  	<< setw(11) << dodge_chance 	      << endl;
	cout << "Move Speed: 	"	<< setw(10) << move_speed 	      << endl;
	cout << "Attack Range:  "	<< setw(11) << attack_range  	      << endl;
	cout << "Sight Range:   "	<< setw(11) << sight_range 	      << endl;
	cout << "HP Regen:      "	<< setw(11) << hp_regen 	      << endl;
	cout << "Mana Regen:    "	<< setw(11) << mana_regen 	      << endl;
	cout << "Experience:    " 	<< setw(11) << float(experience)      << endl;
	cout << "Max Experience:" 	<< setw(11) << float(max_experience)  << endl;
	cout << "Coords         "  	<< setw(10) << cordX << " / "<< cordY << endl;
	cout << "Cost:          "	<< setw(2)  << cost[0] << " / " << cost[1] << " / " << cost[2] << endl;
	cout << endl;
}

void Object::ViewHp(){
		cout << "______  Unit " <<  name << " have " <<
			float(health) << " health" << endl;
}

void Object::Gain_exp(float x){
	experience += x;
	if(experience >= max_experience) Object::Gain_level();
}

void Object::Gain_level(){
	experience = 0;
	level += 1;
	attack = attack * 1.2;
	health = health * 1.3;
	mana  = mana * 1.2;
	max_experience = max_experience * 1.4;
	move_speed = move_speed * 1.1;
	sight_range = sight_range * 1.1;
	attack_range = attack_range * 1.1;
	max_move_speed 	= max_move_speed * 1.1;
	max_attack_range = max_attack_range * 1.1;
	max_sight_range = max_sight_range  * 1.1;
	max_health = max_health * 1.3;
	max_mana = max_mana * 1.2;
	max_attack = max_attack * 1.2;
}

// TODO: ?
void Object::Take_dmg(float dmg, string attack, int crit){
	bool critical = false; 		// показатель критического удара
	if(attack == armor_type) 			dmg *= 1.25; // проверка на уязвимости типа брони против типа атаки
	if((rand() % 100 + 1) <= crit){
		dmg *= 2; 				// если крит, то увеличиваем уров вдвое
		critical = true; 		// и показатель крита ставим
	}
	if((rand() % 100 + 1) > dodge_chance){ 		// если не уклонился
		health -= dmg;
		if(critical)	cout << "______  " << name << "("  // если крит
			<< health << ")" << " taken CRITICLAL damage = " << float(dmg) << endl;
		else cout << "______  " << name << "(" << health << ")" // если не крит
			<< " taken damage = " << float(dmg) << endl;	
	}
	else cout << "______  " << name << " EVOID attack" << endl; // если уклоненился
}


float Object::Get_exp_amount(Object *target){
	float x = float((target->health + (target->mana* 3) + (target->attack* 5))/7);
	return x;
}



bool Object::DeadOrNot(Object *target){
	if(target -> health <= 0){	// Здоровье на нуле -> мертв
		delete target; 			// удаляем убитую цель
		return true;
	}
	else false;	
}



bool Object::TargetInAttackRange(Object *target1, Object *target2){	
	int distance = RangeDetect(target1,target2);		// растояние между двумя объектами	
	// может ли первое существо нанести повреждение второму
	if(distance <= target1-> attack_range) return true;
	else 
		return false;	
}



int Object::RangeDetect(Object *target1, Object *target2){
 	return sqrt(pow(abs(target1->cordX - target2->cordX), 2) + pow(abs(target1->cordY - target2->cordY), 2));
}



bool Object::TargetIsEnemy(Object *target1, Object *target2){
	if(target1->race != target2->race) return true;
	return false;
}


void Object::Fight(Object *target1, Object *target2){
	//Опыт объявлен здесь, так как в дальнейшем проигравший объект будет уничтожен.
	float exp_is_target1 = Get_exp_amount(target1);
	float exp_is_target2 = Get_exp_amount(target2);

if(TargetIsEnemy(target1 , target2)){ // если расы объектов различны
		cout << "	This is an alien, go to attack!" << endl;
	
	while(true){

		// TODO: Если выбор того кто первый будет наносить удар выбирается не случайно, необходимо будет изменить этот фрагмент	
		int int_rand = rand()%2;
		if(int_rand == 0){
			cout << "Situation №1 " << endl;
			//Может ли Существо №1 нанести повреждение существу №2
			if(TargetInAttackRange(target1, target2)){
				cout << "  Essence " << target1 -> name <<" puts damage " << target2 -> name << endl;
				target2->Take_dmg(target1->attack, target1->attack_type[1], target1->crit_chance);
				}
			//Может ли Существо #2 нанести повреждение существу №1
			if(TargetInAttackRange(target2, target1)){
			cout << "  Essence " << target2 -> name <<" puts damage " << target1 -> name << endl;
				target1->Take_dmg(target2->attack, target2->attack_type[1], target2->crit_chance);
				}
		}
		else{
			cout << "Situation №2 " << endl;
			//Может ли Существо #2 нанести повреждение существу №1
			if(TargetInAttackRange(target2, target1)){
			cout << "  Essence " << target2 -> name <<" puts damage " << target1 -> name << endl;
				target1->Take_dmg(target2->attack, target2->attack_type[1], target2->crit_chance);
				}
			//Может ли Существо №1 нанести повреждение существу №2
			if(TargetInAttackRange(target1, target2)){
				cout << "  Essence " << target1 -> name <<" puts damage " << target2 -> name << endl;	
				target2->Take_dmg(target1->attack, target1->attack_type[1], target1->crit_chance);
			}
		}		
		if(DeadOrNot(target1) != 0){		//Существо №1 мертво?
			cout << target2->name << "(" << target2->health << ")" << " WON" << endl;	
			target2 -> Gain_exp(exp_is_target1);	//Дать опыт Существу №2
			break;	
		}
		if(DeadOrNot(target2) != 0){		//Существо №2 мертво?
			cout << target1->name << "(" << target1->health << ")" << " WON" << endl;	
			target1 -> Gain_exp(exp_is_target2);	//Дать опыт Существу №1
			break;
		}
	
	}//while
		
	}//if
	else
		cout << "	This is our race, to ignore." << endl;	
}


bool Object::TargetInSight(int distance, int sight_distance){
	if(distance <= sight_distance) return true;
	return false;
}



void Object::Buf(Object *target, int arg, string type, float amount){
	if(type == "+"){
		switch(arg){	
			case 1:{
			    target->health += amount;
			    if(target->health < 0) target->health = 0;
			}
			case 2:{
			    target->mana += amount;
			    if(target->mana < 0) target->mana = 0;
			}
			case 3:{
				target->attack += amount;
			    if(target->attack < 0) target->attack = 0;
			}
			case 4:{
			    target->move_speed += amount;
			    if(target->move_speed < 0) target->move_speed = 0;
			}
			case 5:{
			    target->attack_range += amount;
			    if(target->attack_range < 0) target->attack_range = 0;
			}
			case 6:{
			    target->sight_range += amount;
			    if(target->sight_range < 0) target->sight_range = 0;
			}
		}
	}
	else if(type == "*"){
		switch(arg){	
			case 1:         target->health 			*= amount;
			case 2:			target->mana 			*= amount;
			case 3:			target->attack 			*= amount;
			case 4:		    target->move_speed 		*= amount;
			case 5: 	    target->attack_range 	*= amount;
			case 6: 	    target->sight_range 	*= amount;
		}
	}
	else cout << "Bad buf type" << endl;
}

